package orm

import (
	"gorm.io/gorm"
	"hertz_demo/orm/db"
	"hertz_demo/orm/models"
	"sort"
)

type sliceInt64 []int64

func (arr sliceInt64) Len() int {
	return len(arr)
}
func (arr sliceInt64) Less(i, j int) bool {
	return arr[i] < arr[j]
}
func (arr sliceInt64) Swap(i, j int) {
	arr[i], arr[j] = arr[j], arr[i]
}
func GetIntersection(val1 sliceInt64, val2 sliceInt64) (result []int64) {
	sort.Sort(val1)
	sort.Sort(val2)
	i, j := 0, 0
	size1, size2 := len(val1), len(val2)
	for i < size1 && j < size2 {
		if val1[i] == val2[j] {
			result = append(result, val1[i])
			i++
			j++
		} else if val1[i] < val2[j] {
			i++
		} else { // val1[i] > val2[j]
			j++
		}
	}
	return
}

func FollowUser(user *models.User, followUserId int64) error {
	relation := db.FollowRelation{
		UserId:       user.ID,
		FollowUserId: followUserId,
	}
	err := db.DB.Transaction(func(tx *gorm.DB) error {
		var relationId int64 = 0
		tx.Raw("select `id` from `follow_relations` where `user_id`=? and `follow_user_id`=? and is_del=false for update",
			user.ID, followUserId).Scan(&relationId)
		if relationId != 0 {
			return nil
		}
		if err := tx.Model(&db.FollowRelation{}).Create(&relation).Error; err != nil {
			return err
		}
		if err := tx.Model(&db.User{}).Where("id=?", followUserId).Update("follower_count", gorm.Expr("follower_count + ?", 1)).Error; err != nil {
			return err
		}
		if err := tx.Model(&db.User{}).Where("id=?", user.ID).Update("follow_count", gorm.Expr("follow_count + ?", 1)).Error; err != nil {
			return err
		}
		return nil
	})
	// todo: sync redis
	return err
}

func DisFollowUser(user *models.User, followUserId int64) error {
	err := db.DB.Transaction(func(tx *gorm.DB) error {
		var relationId int64 = 0
		tx.Raw("select `id` from `follow_relations` where `user_id`=? and `follow_user_id`=? and is_del=false for update",
			user.ID, followUserId).Scan(&relationId)
		if relationId == 0 {
			return nil
		}
		if err := tx.Model(&db.FollowRelation{}).Where("id=?", relationId).Update("is_del", true).Error; err != nil {
			return err
		}
		if err := tx.Model(&db.User{}).Where("id=?", followUserId).Update("follower_count", gorm.Expr("follower_count - ?", 1)).Error; err != nil {
			return err
		}
		if err := tx.Model(&db.User{}).Where("id=?", user.ID).Update("follow_count", gorm.Expr("follow_count - ?", 1)).Error; err != nil {
			return err
		}
		return nil
	})
	// todo: sync redis
	return err
}

func GetFollowList(user *models.User) ([]models.User, error) {
	var resultList []models.User
	var followList []int64
	if err := db.DB.Model(&db.FollowRelation{}).Select("follow_user_id").Where("user_id=?", user.ID).Find(&followList).Error; err != nil {
		return resultList, err
	}
	var dbUserList []db.User
	if err := db.DB.Model(&db.User{}).Find(&dbUserList, followList).Error; err != nil {
		return resultList, err
	}
	for _, dbUser := range dbUserList {
		modelUser := models.User{
			ID:            dbUser.ID,
			Name:          dbUser.Name,
			FollowCount:   dbUser.FollowCount,
			FollowerCount: dbUser.FollowerCount,
			IsFollow:      true,
		}
		resultList = append(resultList, modelUser)
	}
	return resultList, nil
}

func GetFollowerList(user *models.User) ([]models.User, error) {
	var resultList []models.User
	var followerList []int64
	if err := db.DB.Model(&db.FollowRelation{}).Select("user_id").Where("follow_user_id=?", user.ID).Find(&followerList).Error; err != nil {
		return resultList, err
	}
	var followList []int64
	if err := db.DB.Model(&db.FollowRelation{}).Select("follow_user_id").Where("user_id=?", user.ID).Find(&followList).Error; err != nil {
		return resultList, err
	}
	friendList := GetIntersection(followerList, followList)

	var dbUserList []db.User
	if err := db.DB.Model(&db.User{}).Find(&dbUserList, followList).Error; err != nil {
		return resultList, err
	}
	for _, dbUser := range dbUserList {
		isFollow := false
		for _, id := range friendList {
			if id == dbUser.ID {
				isFollow = true
				break
			}
		}
		modelUser := models.User{
			ID:            dbUser.ID,
			Name:          dbUser.Name,
			FollowCount:   dbUser.FollowCount,
			FollowerCount: dbUser.FollowerCount,
			IsFollow:      isFollow,
		}
		resultList = append(resultList, modelUser)
	}
	return resultList, nil
}

func GetFriendList(user *models.User) ([]models.User, error) {
	var resultList []models.User
	var followerList []int64
	if err := db.DB.Model(&db.FollowRelation{}).Select("user_id").Where("follow_user_id=?", user.ID).Find(&followerList).Error; err != nil {
		return resultList, err
	}
	var followList []int64
	if err := db.DB.Model(&db.FollowRelation{}).Select("follow_user_id").Where("user_id=?", user.ID).Find(&followList).Error; err != nil {
		return resultList, err
	}
	friendList := GetIntersection(followerList, followList)

	var dbUserList []db.User
	if err := db.DB.Model(&db.User{}).Find(&dbUserList, friendList).Error; err != nil {
		return resultList, err
	}
	for _, dbUser := range dbUserList {
		modelUser := models.User{
			ID:            dbUser.ID,
			Name:          dbUser.Name,
			FollowCount:   dbUser.FollowCount,
			FollowerCount: dbUser.FollowerCount,
			IsFollow:      true,
		}
		resultList = append(resultList, modelUser)
	}
	return resultList, nil
}
